package hebClustering;

import hebClustering.clusteringAlgorithms.*;
import hebClustering.documentTypes.Document;
import hebClustering.nlp.*;
import hebClustering.searchEngines.Bing;
import hebClustering.searchEngines.ISearchEngine;
import hebClustering.searchEngines.WebsiteInformation;
import hebClustering.vectorSpace.MetricSpace;
import hebClustering.vectorSpace.distances.*;
import hebClustering.web.*;

import java.io.IOException;
import java.util.Set;

public class ClusterSearch {
	
	private static INLP nlp = new HebrewNLP();

	public static boolean debug = false;
	public static long currentTime;

	private ClusterSet clusteringResult;
	private MetricSpace metricSpace;
	private String query;

	/**	
	 *	Performs a clustering algorithm on a dataset.
	 *
	 *	@param input - A search string, if the debug flag is false, or a premade dataset's name otherwise.
	 *	@param numOfResults - Number of search results to be searched.
	 *	@param clusteringAlgorithm - A string representation of the desired clustering algorithm used.<br>
	 *	Available values:
	 * <a href="http://en.wikipedia.org/wiki/K-means%2B%2B" target="_blank" title="K-Means Plus Plus">KPP</a> ,  
	 * <a href="http://en.wikipedia.org/wiki/K-means_clustering" target="_blank" title="K-Means">K</a> ,
	 * <a href="http://en.wikipedia.org/wiki/Hierarchical_clustering" target="_blank" title="Hierarchical">H</a> ,    
	 * <a href="http://en.wikipedia.org/wiki/Cluster_analysis#Spectral_clustering" target="_blank" title="Spectral">S</a> ,
	 * <a title="Bisecting K-Means">BK</a> ,
	 * <a title="Hierarchical K-Means">HK</a> ,
	 * <a title="Epsilon-Delta Means">EDM</a> ,      
	 * <a title="Minimum Spanning Tree">MST</a>.
	 * 	
	 * 	@param strDistance - A string representation of the desired distance function.<br>
	 * 	Available values:
	 * 	<a href="http://en.wikipedia.org/wiki/Chebyshev_distance" target="_blank" title="Chebyshev">CHE</a> ,
	 * 	<a href="http://en.wikipedia.org/wiki/Euclidean_distance" target="_blank" title="Euclidean">EUC</a> ,
	 * 	<a href="http://en.wikipedia.org/wiki/Taxicab_geometry" target="_blank" title="Manhattan">MAN</a>.
	 * 
	 * 	@param K - Maximal number of clusters.
	 * 	@param isDebug - When flaged, the function will cluster over the search results received
	 *  from the search engine when searching for the word in the input parameter, 
	 *  otherwise will cluster over the premade dataset with the name in input.
	 *  
	 *  
	 * 
	 */
	public void search(String input,int numOfResults, String clusteringAlgorithm, String strDistance, int K ,boolean isDebug){

		currentTime = System.currentTimeMillis();
		query = input;
		
		System.out.println("Starting the process...");
		Corpus corpus = Corpus.getCorpus();
		System.err.println("time for NLP builder: " + (System.currentTimeMillis() - currentTime));
		Set<Document> documentSet;
		if (!isDebug){
			ISearchEngine engine = new Bing();
			Set<WebsiteInformation> urlResults = engine.search(input.replaceAll("\'", ""),numOfResults);
			System.err.println("time for search: " + (System.currentTimeMillis() - currentTime));
			documentSet = ((HebrewNLP)nlp).urlsToDocuments(urlResults);
		}else{
			documentSet = ((HebrewNLP)nlp).rawFilesToDocuments("../heb-clustering/lib/Datasets/" + input);
		}


		System.err.println("time for textToDocuments: " + (System.currentTimeMillis() - currentTime));
		corpus.addDocuments(documentSet);
		System.err.println("time for adding to the corpus: " + (System.currentTimeMillis() - currentTime));

		metricSpace = new MetricSpace(corpus.getNumOfTokens(),documentSet);
		System.err.println("time for MetricSpace builder: " + (System.currentTimeMillis() - currentTime));

		final int I = 60;
		final int P = 5;

		IDistance distance = parseDistance(strDistance);
		IClusteringAlgorithm algorithm = parseClusteringAlgorithm(clusteringAlgorithm,K,I,P,distance);
		
		
		clusteringResult = algorithm.cluster(metricSpace.getVectors());
		clusteringResult.setRepresentatives();
		metricSpace.setClusters(clusteringResult);
		System.err.println("time for clustering: " + (System.currentTimeMillis() - currentTime));

		System.err.println("time for setting representatives: " + (System.currentTimeMillis() - currentTime));

	}

	/**
	 * 
	 * A main program, that given arguments, activates {@link #search(String, int, String, String, int, boolean) search} on them.
	 * Made for debug purposes.
	 * 
	 * @param args - A string array that contains arguments for {@link #search(String, int, String, String, int, boolean) search}.
	 */
	public static void main(String[] args){

		int numOfResults = 0;
		String clusteringAlgorithm = "EDM";
		String strDistance = "MAN";
		String query = args[0];
		if (!debug){

			if (args.length < 2){
				System.err.println("Not enough arguments!");
				System.exit(0);
			}

			numOfResults = Integer.valueOf(args[1]);
			if (args.length >= 3)
				clusteringAlgorithm = args[2];
			if (args.length >= 4)
				strDistance = args[3];
		}
		final int K = 14;
		ClusterSearch searchSession = new ClusterSearch();
		searchSession.search(query, numOfResults, clusteringAlgorithm,strDistance,K,debug);

		System.out.println(searchSession.metricSpace);
		System.out.println(searchSession.metricSpace.printDebug(parseDistance(strDistance)));

	}

	private static IClusteringAlgorithm parseClusteringAlgorithm(String clusteringAlgorithm, int K, int I, int P, IDistance distance) {
		if (clusteringAlgorithm.equals("KPP"))
			return new KMeansPPClustering(K, I, distance);
		else if (clusteringAlgorithm.equals("K"))
			return new KMeansClustering(K, I, distance);
		else if (clusteringAlgorithm.equals("BK"))
			return new BisectingKMeansClustering(K, distance);
		else if (clusteringAlgorithm.equals("S"))
			return new SpectralClustering(K, I, distance);
		else if (clusteringAlgorithm.equals("H"))
			return new HierarchicalClustering(K, distance);
		else if (clusteringAlgorithm.equals("HK"))
			return new HierarchicalKMeansClustering(K,I, P, distance);
		else if(clusteringAlgorithm.equals("EDM"))
			return new EpsilonDeltaMeansClustering(15,0.27, distance);
		else if(clusteringAlgorithm.equals("MST"))
			return new MSTClustering(distance,K);
		else return null;
	}
	
	private static IDistance parseDistance(String distance){
		if (distance.equals("CHE"))
			return new ChebyshevDistance();
		else if (distance.equals("EUC"))
			return new EuclidianDistance();
		else if (distance.equals("MAN"))
			return new ManhattanDistance();
		else return null;
	}

	
	/**
	 * Exports the HTML page that contains the clusters to a predefined HTML file.
	 */
	public void toHTML(){
		try {
			HTMLExport export = new HTMLExport(query);
			export.metricSpaceToHTML(metricSpace);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}


}